#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

#include "synq.h"

int _synq_create_tree(sST *ptree, sST **tree, unsigned int curdir, unsigned int numdirs);

sSynq *synq_get_status(char **dirs, unsigned int numdirs) {
    if(!dirs || !numdirs) return NULL;

    unsigned int curdir;
    sSynq *synq=(sSynq *)calloc(1, sizeof(sSynq));
    if(!synq) return NULL;

    // init the dirs list (make them absolute if they are relative)
    if(DL_init(&synq->dirs, dirs, numdirs)) {
        free(synq);
        return NULL;
    }

    // create tree with stats of files for each dir
    for(curdir=0; curdir<synq->dirs.numdirs; curdir++) {
        if(chdir(synq->dirs.dirs[curdir])) {
            printf("fail chdir(\"%s\")\n", synq->dirs.dirs[curdir]);
            continue;
        }
        if(_synq_create_tree(NULL, &synq->tree, curdir, synq->dirs.numdirs)) {
            synq_free(synq);
            return NULL;
        }
    }

    // update the status of each file in the tree
    if(synq_update_status(synq)) {
        synq_free(synq);
        return NULL;
    }

    return synq;
}

int _synq_create_tree(sST *ptree, sST **tree, unsigned int curdir, unsigned int numdirs) {   // no check to be fast
    DIR *cwd=NULL;
    struct dirent *cde=NULL;

    cwd=opendir(".");
    if(!cwd) return 1;

    while((cde=readdir(cwd))) { // for each file in the current directory
        if(!strcmp(cde->d_name, ".") || !strcmp(cde->d_name, "..")) continue;

        // search the file in the virtual tree
        sST *tmp=*tree;
        while(tmp) {
            if(!strcmp(tmp->name, cde->d_name)) break;  // ou strcasecmp suivant le mode
            tmp=tmp->next;
        }

        if(!tmp) {  // doesn't exists yet => create it
            char *name=strdup(cde->d_name);
            if(!name) {
                printf("fail strdup(\"%s\")\n", cde->d_name);
                continue;
            }

            tmp=ST_append(tree, name, NULL);
            if (!tmp) {
                printf("fail ST_append(,\"%s\",)\n", name);
                continue;
            }
            tmp->parent=ptree;
        }

        if(!tmp->files) {
            tmp->files=(sFS *)calloc(numdirs, sizeof(sFS));
            if(!tmp->files) {
                printf("fail calloc(%d,%d)\n", numdirs, sizeof(sFS));
                continue;
            }
        }

        if(stat(cde->d_name, &tmp->files[curdir].stat)) {
            printf("fail stat(\"%s\")\n", cde->d_name);
            continue;
        }

        if(S_ISDIR(tmp->files[curdir].stat.st_mode)) {
            if(chdir(tmp->name)) {
                printf("fail chdir(\"%s\")\n", tmp->name);
                continue;
            }

            _synq_create_tree(tmp, &tmp->child, curdir, numdirs);

            if(chdir("..")) {
                printf("fail chdir(\"..\")\n");
                continue;
            }
        }
    }

    closedir(cwd);
    return 0;
}

int _synq_update_status(sST *tree, unsigned int numdirs);

int synq_update_status(sSynq *synq) {
    if(!synq || !synq->tree || !synq->dirs.numdirs) return 1;

    return _synq_update_status(synq->tree, synq->dirs.numdirs);
}

int _synq_update_status(sST *tree, unsigned int numdirs) {   // no check to be fast
    while(tree) {
        if(FS_set(tree->files, numdirs)) return 1;

        if(tree->child && _synq_update_status(tree->child, numdirs)) return 1;

        tree=tree->next;
    }

    return 0;
}

sSynq *synq_dup(sSynq *src) {
    sSynq *dst=(sSynq *)calloc(1, sizeof(sSynq));
    if(!dst) return NULL;

    dst->tree=ST_dup(src->tree, src->dirs.numdirs);
    if(!dst->tree) {
        synq_free(dst);
        return NULL;
    }
    if(DL_copy(&src->dirs, &dst->dirs)) {
        synq_free(dst);
        return NULL;
    }

    return dst;
}

void synq_free(sSynq *synq) {
    if(!synq) return;

    if(synq->tree) ST_free_all_data(synq->tree);
    DL_deinit(&synq->dirs);

    free(synq);
}

